package allod.gui.som

import collection.mutable
import edu.umd.cs.piccolo.{PLayer, PCanvas}
import swing.Publisher
import java.awt.{Paint, Color}
import edu.umd.cs.piccolo.nodes.PPath
import swing.event.Event
import allod.som.upgma.{CompositeCluster, IdentityCluster, Cluster}
import edu.umd.cs.piccolo.event.{PZoomEventHandler, PInputEvent, PBasicInputEventHandler}

/**
 * Created by IntelliJ IDEA.
 * User: allod
 * Date: 13 бер 2010
 * Time: 14:24:32
 */

class Dendrogram(root: Cluster) extends PCanvas with Publisher {
  private val identityClusters: mutable.Map[Cluster, ClusterNode] = createIdentityNodes(root)
  private val selectedClusters = mutable.Set.empty[Cluster] + root
  private val zoom = 500 / root.height

  val rootNode = ClusterNode(root)
  rootNode.color = Color.YELLOW

  setZoomEventHandler(new PZoomEventHandler() {
    override def processEvent(evt: PInputEvent, i: Int) {
      if (evt.isMouseWheelEvent()) {
        val s = 1D - 0.25 * evt.getWheelRotation()
        val p = evt.getPosition()
        evt.getCamera().scaleViewAboutPoint(s, p.getX(), p.getY())
      }
    }
  })

  val edgeLayer = new PLayer()
  rootNode.paintEdges(edgeLayer)

  val nodeLayer = getLayer()
  rootNode.paintNodes(nodeLayer)
  getCamera.addLayer(0, edgeLayer)
  nodeLayer.addInputEventListener(new PBasicInputEventHandler {
    override def mouseEntered(p1: PInputEvent) = {
      p1.getPickedNode.setPaint(Color.BLACK)
    }

    override def mouseExited(p1: PInputEvent) = {
      try {
        val clusterNode = p1.getPickedNode.getParent().asInstanceOf[ClusterNode]
        clusterNode.setPaint(clusterNode.color)
      } catch {
        case e: ClassCastException => System.err.println(e)
      }
    }

    override def mouseClicked(p1: PInputEvent) = {
      try {
        val clusterNode = p1.getPickedNode.getParent().asInstanceOf[CompositeClusterNode]
        clusterNode.changeColor()
      } catch {
        case e: ClassCastException => System.err.println("Unexpected node type")
      }
    }
  })

  def getSelectedClusters(): Seq[Cluster] = selectedClusters.toSeq

  private def createIdentityNodes(cluster: Cluster) = {
    val identityClusterMap = mutable.Map.empty[Cluster, ClusterNode]
    val clusters = cluster.getIdentityClusters
    var xLocation = 0
    for (cluster <- clusters) {
      val node = new ClusterNode(cluster) {
        private var nodeColor = Color.WHITE

        def color = nodeColor

        def color_=(newColor: Color) {
          newColor match {
            case Color.RED =>
              selectedClusters - this.cluster
            case Color.YELLOW =>
              selectedClusters + this.cluster
            case _ =>
              selectedClusters - this.cluster
          }
          nodeColor = newColor
          setPaint(nodeColor)
        }

        def paintNodes(nodeLayer: PLayer) = {}

        def paintEdges(edgeLayer: PLayer) = {}

        val clusterHeight = 0

        val centerPoint = xLocation

        val rightPoint = xLocation

        val leftPoint = xLocation
      }
      xLocation += 2
      identityClusterMap += cluster -> node
    }
    identityClusterMap
  }

  object ClusterNode {
    def apply(cluster: Cluster): ClusterNode =
      cluster match {
        case identity: IdentityCluster => identityClusters(identity)
        case composite: CompositeCluster => new CompositeClusterNode(composite)
      }
  }

  abstract class ClusterNode(val cluster: Cluster) extends PPath {
    var color: Color

    def leftPoint: Int

    def rightPoint: Int

    def centerPoint: Int

    def clusterHeight: Int

    def paintEdges(edgeLayer: PLayer): Unit

    def paintNodes(nodeLayer: PLayer): Unit
  }

  private class CompositeClusterNode(override val cluster: CompositeCluster) extends ClusterNode(cluster) {
    private var nodeColor = Color.WHITE

    def color = nodeColor

    def color_=(newColor: Color) {
      newColor match {
        case Color.RED =>
          selectedClusters - this.cluster
          leftChild.color = Color.YELLOW
          rightChild.color = Color.YELLOW
        case Color.YELLOW =>
          selectedClusters + this.cluster
          nodeColor match {
            case Color.RED =>
              leftChild.color = Color.YELLOW
              rightChild.color = Color.YELLOW
              leftChild.color = Color.WHITE
              rightChild.color = Color.WHITE
            case _ =>
          }
        case _ =>
          selectedClusters - this.cluster
      }
      nodeColor = newColor
      setPaint(nodeColor)
    }

    val leftChild: ClusterNode = ClusterNode(cluster.leftChild)

    val rightChild: ClusterNode = ClusterNode(cluster.rightChild)

    val clusterHeight = (cluster.height * zoom).asInstanceOf[Int]

    val rightPoint = rightChild.centerPoint

    val leftPoint = leftChild.centerPoint

    val centerPoint = (leftPoint + rightPoint) / 2

    val edges = new PPath()
    edges.moveTo(leftPoint, -leftChild.clusterHeight)
    edges.lineTo(leftPoint, -clusterHeight)
    edges.lineTo(rightPoint, -clusterHeight)
    edges.lineTo(rightPoint, -rightChild.clusterHeight)

    private val radius = Math.pow(Math.log(rightPoint - leftPoint + 4), 2).asInstanceOf[Float]
    private val node = PPath.createEllipse(centerPoint - radius / 2, -clusterHeight - radius / 2, radius, radius)
    addChild(node)

    def paintEdges(edgeLayer: PLayer) {
      edgeLayer.addChild(edges)
      leftChild.paintEdges(edgeLayer)
      rightChild.paintEdges(edgeLayer)
    }

    def paintNodes(nodeLayer: PLayer) {
      nodeLayer.addChild(this)
      leftChild.paintNodes(nodeLayer)
      rightChild.paintNodes(nodeLayer)
    }

    override def setPaint(p1: Paint) = {
      super.setPaint(p1)
      node.setPaint(p1)
    }

    def changeColor() {
      nodeColor match {
        case Color.RED =>
          color = Color.YELLOW
        case Color.YELLOW =>
          color = Color.RED
        case _ =>
      }
      publish(new DendrogramEvent(selectedClusters.toSeq))
    }
  }
}

case class DendrogramEvent(val clusters: Seq[Cluster]) extends Event